# _*_ coding:utf-8 _*_

__all__ = ['get_template', 'register_template']

import re
import StrUtils
import CommonUtils

actionTemplates = {}

'''
plan: 放在生成角本的最前面
main: 放在main函数中
actor: 动作表达式
final: 针对计算结果数据每行的整理动作
gfinal: 针对计算结果数据总体的整理动作（每次分析只执行一次）
xy   : x / y / ''； 只限定x或者y或者不限定
argd : 缺省参数。没有缺省参数，外界又没提供一个，运算时会抛异常
merge: 表示在数据分析过程中，如果涉及到中间结果的合并，分析器该如何动作
'''
template_sum = {
    'xy'   : '',
    'plan' : 'import numpy as np',
    'main' : '',
    'actor': '''_src.data[{_y__}, {_x__}] = np.sum([_src.data[{_y__}, {_x__}], {valExpr}])''',
    'final': '',
    'gfinal': '',
    'merge': '''
_src.data[{_y__}, {_x__}] += np.sum([x.data[{_y__}, {_x__}] for x in _src.slaves])
''',
}

template_avg = {
    'xy'   : '',
    'plan' : 'import numpy as np',
    'main' : '',
    'actor': '''_src.data[{_y__}, {_x__}] = np.sum([_src.data[{_y__}, {_x__}], {valExpr}]); _src.extdata[{_y__}, {_x__}] += 1''',
    'final': '''_src.data[{_y__}, {_x__}] /= _src.extdata[{_y__}, {_x__}]''',
    'gfinal': '',
    'merge': '''
_src.data[{_y__}, {_x__}] += np.sum([x.data[{_y__}, {_x__}] for x in _src.slaves])
_src.extdata[{_y__}, {_x__}] += np.sum([x.extdata[{_y__}, {_x__}] for x in _src.slaves])
''',
}

template_max = {
    'xy'   : '',
    'plan' : '',
    'main' : '''
for i in range(_src.numY):
    _src.data[i, {_x__}] = float('-inf')
''',
    'actor': '''_src.data[{_y__}, {_x__}] = max(_src.data[{_y__}, {_x__}], {valExpr})''',
    'final': '',
    'gfinal': '',
    'merge': '''
_src.data[{_y__}, {_x__}] = max(_src.data[{_y__}, {_x__}], *[x.data[{_y__}, {_x__}] for x in _src.slaves])
''',
}

template_min = {
    'xy'   : '',
    'plan' : '',
    'main' : '''
for i in range(_src.numY):
    _src.data[i, {_x__}] = float('inf')
''',
    'actor': '''_src.data[{_y__}, {_x__}] = min(_src.data[{_y__}, {_x__}], {valExpr})''',
    'final': '',
    'gfinal': '',
    'merge': '''
_src.data[{_y__}, {_x__}] = min(_src.data[{_y__}, {_x__}], *[x.data[{_y__}, {_x__}] for x in _src.slaves])
''',
}

'''
计算一个多维数组的任意百分比分位数，
'''
template_percentile = {
    'xy'   : 'x',
    'argd' : {'conf': [50]},          # 缺省参数。没有缺省参数，外界又没提供一个，就只剩异常了
    'plan' : 'import numpy as np',
    'main' : '',
    'actor': '''_src.moredata[{_y__}, {_x__}].append({valExpr})''',
    'final': '''_src.data[{_y__}, {_x__}] = cu.tryExpr("np.percentile(_src.moredata[{_y__}, {_x__}], {conf})")''',
    'gfinal': '',
    'merge': '''
_src.moredata[{_y__}, {_x__}].extend([x.moredata[{_y__}, {_x__}] for x in _src.slaves])
''',
}

'''
一次生成三个数的动作, 此处的百分位是从小到大排列
'''
template_stats3 = {
    'xy'   : 'x',
    'plan' : 'import numpy as np',
    'main' : '',
    'actor': '''_src.moredata[{_y__}, {_x__}].append({valExpr})''',
    'final': '''
_src.data[{_y__}, {_x__}] = cu.tryExpr("np.percentile(_src.moredata[{_y__}, {_x__}], [25, 50, 75])[0]")
_src.data[{_y__}, {_x__}+1] = cu.tryExpr("np.percentile(_src.moredata[{_y__}, {_x__}], [25, 50, 75])[1]")
_src.data[{_y__}, {_x__}+2] = cu.tryExpr("np.percentile(_src.moredata[{_y__}, {_x__}], [25, 50, 75])[2]")
''',
    'gfinal': '',
    'merge': '''
[_src.moredata[{_y__}, {_x__}].append(y) for x in _src.slaves for y in x.moredata[{_y__}, {_x__}]]
''',
}

'''
全局计算一个多维数组的任意百分比分位数，此处的百分位是从小到大排列
'''
template_gstats = {
    'xy'   : '',
    'plan' : 'import numpy as np',
    'main' : '',
    'actor': '''_src.appendDictData('{_x__}', {valExpr})''',
    'final': '',
    'gfinal': '''_src.dictdata['percentile_{_x__}'] = cu.tryExpr("np.percentile(_src.getDictData('{_x__}'), [25, 50, 75])")''',
    'merge': '''
_src.mergeDictData([x.dictdata for x in _src.slaves])
''',
}

'''
pearsonr(x,y)
功能：
    计算特征与目标变量之间的相关度
参数说明：
    1）输入：x为特征，y为目标变量.
    2）输出：r： 相关系数 [-1，1]之间，p-value: p值。
         注： p值越小，表示相关系数越显著，一般p值在500个样本以上时有较高的可靠性。
输出示例：
    (0.71824836862138408, 7.3240173129983507e-49)
    (0.057964292079338155, 0.31700993885324752)
'''
template_pearsonr = {
    'xy'   : '',
    'plan' : '''import scipy.stats as stats
import MiscUtils''',
    'main' : '',
    'actor': '_src.moredata[{_y__}, {_x__}].append({valExpr})',
    'final': '''
x=MiscUtils.arrSplit(_src.moredata[{_y__}, {_x__}])
if x and (len(x) >= 2) and x[0] and len(x[0]) >= 2 and x[1] and len(x[1]) >= 2:
    _src.data[{_y__}, {_x__}] = stats.pearsonr(x[0], x[1])[0]
''',
    'gfinal': '',
    'merge': '''
''',
}

'''
记录当前数据源信息
'''
template_record = {
    'xy'   : 'x',
    'argd' : '',
    'plan' : '',
    'main' : '',
    'actor': '''
_expr__ = {valExpr}
_src.moredata[{_y__}, {_x__}].append(_expr__)
''',
    'final': '',
    'gfinal': '',
    'merge': '''
[_src.moredata[{_y__}, {_x__}].append(y) for x in _src.slaves for y in x.moredata[{_y__}, {_x__}]]
''',
}

'''
小计
'''
template_local_sum = {
    'xy'   : 'x',
    'argd' : '',
    'plan' : '''import numpy as np
import MiscUtils''',
    'main' : '',
    'actor': '',
    'final': '',
    'gfinal': 'MiscUtils.arr2d_slice_act(_src.data, {_y__}, {valExpr}[0], {_x__}, {_x__}, np.sum)',
    'merge': '',
}

def switch_template(x):
    try:
        return actionTemplates[x]
    except KeyError:
        return {}


def get_template(_src, strExpr, x, xy=''):
    #
    # strExpr示例： $act1 valExpr1; $act2 valExpr2; $act3 valExpr3
    #
    plan = ''
    main = ''
    actor = ''
    final = ''
    gfinal = ''
    merge = ''

    while True:
        label = re.match(r'^\$\w+', strExpr, re.I)
        if label is None:
            return ('',)*6
        pos = label.span()[1]
        fun = label[0]

        try:
            strExpr_n = strExpr[pos:]
            status, strExpr = StrUtils.getStatementContent(strExpr_n, getComment=False, valNone=False, separator=';')
        except (BaseException) as exc:
            return ('',)*6

        t = switch_template(fun)
        if t:
            if xy:
                xy_ = t['xy']
                if ((xy != xy_) and (xy_ != '')):
                    return ('',)*6

            # ActionTemplate中的设置为最低优先级，稍高优先级为环境参数，最高优先级为角本中的语法
            dictExpr = {'_y__' : '_src.curY', '_x__' : x}
            dict_xy = CommonUtils.getDict('argd', t , {})
            dictExpr.update(dict_xy)
            if _src.parameters:
                dictExpr.update(_src.parameters)
            if (strExpr[0] == '{'):
                dict_ = eval(strExpr)
                dictExpr.update(dict_)
                valExpr = CommonUtils.getDict('_p__', dict_ , '')
                if valExpr != '':
                    strExpr = valExpr

            if t['plan']:
                plan += t['plan'].format(valExpr=strExpr, **dictExpr) + ';'
            if t['main']:
                main += t['main'].format(valExpr=strExpr, **dictExpr) + ';'
            if t['actor']:
                actor += t['actor'].format(valExpr=strExpr, **dictExpr) + ';'
            if t['final']:
                final += t['final'].format(valExpr=strExpr, **dictExpr) + ';'
            if t['gfinal']:
                gfinal += t['gfinal'].format(valExpr=strExpr, **dictExpr) + ';'
            if t['merge']:
                merge += t['merge'].format(valExpr=strExpr, **dictExpr) + ';'
        else:
            action = switch_keyword(fun)
            if action:
                status, info = action(_src, strExpr)
                if (status != 0):
                    return ('',)*6
                else:
                    actor += 'pass;'
            else:
                return ('',)*6

        status, strExpr = StrUtils.getStatementContent(strExpr_n, getComment=True, valNone=False, separator=';')
        pos = 0

        if not strExpr:
            return plan, main, actor, final, gfinal, merge

    return ('',)*6


def sr_markit(_src, statement):
    if statement:
        _src.markit[str(statement)] = (_src.numY, _src.numX)
    else:
        return 1, ''
    return 0, ''

def switch_keyword(x):
    try:
        return {
            '$markit': sr_markit,
        }[x.lower()]
    except KeyError:
        return ''


def register_template(name, template):
    temp = {}
    for field in ('actor',):
        s = template.get(field, '')
        if isinstance(s, str):
            s = s.strip()
            if s:
                temp[field] = s
            else:
                temp[field] = 'pass'
        else:
            temp[field] = s

    for field in ('xy', 'argd', 'plan', 'main', 'final', 'gfinal', 'merge', ):
        s = template.get(field, '')
        if isinstance(s, str):
            temp[field] = s.strip()
        else:
            temp[field] = s
    actionTemplates[name] = temp


register_template('$sum', template_sum)
register_template('$avg', template_avg)
register_template('$max', template_max)
register_template('$min', template_min)
register_template('$percentile', template_percentile)
register_template('$stats3', template_stats3)
register_template('$gstats', template_gstats)
register_template('$pearsonr', template_pearsonr)
register_template('$record', template_record)
register_template('$lsum', template_local_sum)


if __name__ == '__main__':
    print ("switch_template('$avg')")
    print (switch_template('$avg'))
    print ("\nget_template('$avg 1 expr')")
    print (get_template('$avg expr1; $avg expr2; $avg expr3', 888))
